#!/usr/bin/python3

import contextlib
import functools

'''
说明：
1、本章为扩展知识（超出教程范围），讲解自定义装饰器；
2、结合ch18中嵌套函数、闭包的概念和例子，更容易理解装饰器。
'''

# 1、with语句，自动关闭流
def my_with():
    with open('__init__.py') as file_object:
        contents = file_object.read()
        print("打印文件内容：\n", contents)
# 调用函数
# my_with()

# 2、 @contextlib.contextmanager
'''
@contextlib.contextmanager标准库中的一个上下文管理器装饰器，用于创建自定义的上下文管理器。
使用场景：日志记录、性能分析、权限控制、缓存等。自定义@contextlib.contextmanager的步骤如下：
1）创建一个生成器函数，使用yield语句将资源的获取和释放分隔开。生成器函数的定义可以包含一些需要在进入和退出上下文时执行的代码。
2）使用@contextlib.contextmanager装饰器修饰生成器函数，将其转换为一个上下文管理器。装饰器会自动处理资源的获取和释放，
   并将生成器函数的代码分为两部分：进入上下文的代码和退出上下文的代码。
3）在需要使用该上下文管理器的地方，使用with语句来创建一个上下文。在with语句块中，可以通过as关键字将上下文管理器的
   返回值赋给一个变量（yield关键字后面的变量），以便在块内部使用。
'''
@contextlib.contextmanager
def my_context_manager():
    # 进入上下文
    print("进入上下文")
    # 获取资源，返回资源
    resource = acquire_resource()
    # yield关键字将资源传递给with语句
    yield resource
    # 释放资源
    release_resource(resource)  # 释放资源
    print("退出上下文")

# 执行with语句中的代码
def do_something_with_resource(resource1):
    print("执行with语句中的代码，yield传过来的资源:", resource1)

# 获取资源
def acquire_resource():
    resource_name = "资源A"
    print("获取资源:", resource_name)
    return resource_name

# 释放资源
def release_resource(res):
    print("释放资源:", res)

# 在需要使用上下文管理器的地方，使用with语句创建上下文
def my_cont():
    # 类似上面的open()方法的with语句，resource1是yield传过来的变量
    with my_context_manager() as resource1:
        # 在上下文中使用资源
        print("开始执行with...as...语句")
        do_something_with_resource(resource1)

# 调用函数
# my_cont()

# 3、@functools.wraps装饰器 ，可以复制原始函数的元信息到装饰器返回的新函数中，保留原函数的属性。
'''
1）作用是在自定义装饰器中，复制原始函数的元信息到装饰器返回的新函数中，从而保留原函数的属性，包括：函数名称、文档字符串、参数信息等
2）参考：参考：https://blog.csdn.net/m0_46967801/article/details/135652565
注意：
1）@functools.wraps要用嵌套函数？修饰的是里面的函数wrapper；使用的时候用的是外面的函数log_decorator
2）wrapper(*args, **kwargs)，位置参数、关键字参数是固定写法？
3）包装器log_decorator(func)参数func接收的参数是一个函数（相加、相乘），并且传递给@functools.wraps(func)
4）wraps装饰器不需要使用with语句
'''
def log_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"调用函数 {func.__name__}, 位置参数是 {args}, 关键字参数是 {kwargs}")
        result = func(*args, **kwargs)
        print(f"函数{func.__name__} 的返回值: {result}")
        return result
    return wrapper

# 两个数字相加
@log_decorator
def add(a, b):
    return a + b

# 两个数字相乘
@log_decorator
def multiply(x, y):
    return x * y

def my_add():
    add(2, 3)
    print(f"两个数字相加的函数的名称: {add.__name__}")
    print("====================分割线=====================")

def my_mul():
    multiply(x=4, y=5)
    print(f"两个数字相乘的函数的名称:: {multiply.__name__}")
    print("====================分割线=====================")

# 调用函数
# my_add()
# my_mul()

